package org.apache.james.hibernate;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;

public class QueryBuilder {
	
	private final static Logger LOG = Logger.getLogger(QueryBuilder.class);

	String select = null;
	
	Set<String> tables = new HashSet<String>();
	
	StringBuilder where = new StringBuilder();
	
	List<Value> params = new ArrayList<Value>();
	
	int position = 0;
	boolean first = true;
	
	static class Value {
		Object value;
		String name;
		int position;
		
		public Value(int pos, Object value) {
			this.position = pos;
			this.value = value;
		}
		
		public Value(String name, Object value) {
			this.value = value;
			this.name = name;
		}
		
		public void addToQuery(Query q) {
			if (name==null) {
				if (value instanceof String) {
					q.setString(position, (String) value);
				} else if (value instanceof Integer) {
					q.setInteger(position, (Integer) value);
				} else if (value instanceof Long) {
					q.setLong(position, (Long) value);
				} else if (value==null) {
					q.setString(position, null);
				} else {
					throw new RuntimeException("unknown type:"+value+", cls:"+value.getClass().getName());
				}
			} else {
				if (value instanceof String) {
					q.setString(name, (String) value);
				} else if (value instanceof Integer) {
					q.setInteger(name, (Integer) value);
				} else if (value instanceof Long) {
					q.setLong(name, (Long) value);
				} else if (value==null) {
					q.setString(name, null);
				} else {
					throw new RuntimeException("unknown type:"+value+", cls:"+value.getClass().getName());
				}
			}
		}
	}
	
	public QueryBuilder() {
	}

	public QueryBuilder(Class entityClass, String name) {
		addTable(entityClass.getName()+' '+name);
	}
	public QueryBuilder(String tableName, String whereFragment) {
		addTable(tableName);
		addWhere(whereFragment);
	}
	
	public boolean addTable(String table) {
		return tables.add(table);
	}
	
	public boolean addTable(Class entityClass, String name) {
		return tables.add(entityClass.getName()+' '+name);
	}
	
	public void setSelect(String select) {
		this.select = select;
	}
	
	public void addWhere(String whereFragment) {
		if (!first) {
			where.append(" and ");
		} else {
			first = false;
		}
		where.append(whereFragment);
	}

	public void addWhereWithParameter(String whereFragment, Object value) {
		addWhere(whereFragment);
		addParameter(value);
	}

	public void addParameter(Object value) {
		params.add(new Value(position, value));
		position ++;
	}

	public void addWhereWithNamedParameter(String whereFragment, String name, Object value) {
		addWhere(whereFragment);
		addNamedParameter(name, value);
	}

	public void addNamedParameter(String name, Object value) {
		params.add(new Value(name, value));
	}
	
	
	
	public Query createQuery(Session session) {
		StringBuilder res = new StringBuilder();
		if (select!=null) {
			res.append("select ");
			res.append(select);
		}
		res.append(" from ");
		boolean firstTable = true;
		for (String table : tables) {
			if (!firstTable) {
				res.append(", ");
			} else {
				firstTable = false;
			}
			res.append(table);
		}
		
		if (where.length()>0) {
			res.append(" where ");
			res.append(where);
		}
		LOG.info("createQuery :"+res.toString());
		Query q = session.createQuery(res.toString());

		for (Value v : params) {
			v.addToQuery(q);
		}
		return q;
	}
}
